using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace MKDS_Course_Editor.Pictures
{
    public partial class NCGR : Form
    {
        PluginInterface.NCLR paleta;
        byte[] ncgr_file;
        byte[] nclr_file;
        List<int> powers = new List<int>();
        public NCGR(string ncgr, string nclr)
        {
            InitializeComponent();
            this.nclr_file = File.ReadAllBytes(nclr);
            this.ncgr_file = File.ReadAllBytes(ncgr);
            for (int i = 1; i < 129; i++)
            {
                powers.Add(i * 8);
            }
            paleta = Tinke.Imagen_NCLR.Leer(nclr_file);
        }
        public enum Orden_Tiles
        {
            No_Tiles,
            Horizontal,
            Vertical
        }
        public struct NCGR_File
        {
            public header Header;
            public rahc RAHC;
            public sopc SOPC;
            public Orden_Tiles orden;
            //public Object other;
            public struct header    // Generic Header
            {
                public char[] id;                   // RCSN = 0x5243534E
                public UInt16 endianess;            // 0xFFFE -> indica little endian
                public UInt16 constant;             // Siempre es 0x0100
                public UInt32 file_size;            // Tamaño total del archivo
                public UInt16 header_size;          // Siempre es 0x10
                public UInt16 nSection;             // Número de secciones
            }
            public struct rahc
            {
                public char[] id;               // Siempre RAHC = 0x52414843
                public UInt32 size_section;
                public UInt16 nTilesY;
                public UInt16 nTilesX;
                public ColorDepth depth;
                public UInt16 unknown1;
                public UInt16 unknown2;
                public UInt32 tiledFlag;
                public UInt32 size_tiledata;
                public UInt32 unknown3;         // Constante siempre 0x18 (24)
                public byte[] ImageData;
                public UInt16 nTiles;       // Campo propio para operaciones más fáciles, resultado de nTilesX * nTilesY ó size_Tiledata / 64
            }
            public struct sopc
            {
                public char[] id;
                public UInt32 size_section;
                public UInt32 unknown1;
                public UInt16 charSize;
                public UInt16 nChar;
            }

            public struct NTFT              // Nintendo Tile Format Tile
            {
                public byte[][] tiles;
                public byte[] nPaleta;
            }
        }
        NCGR_File ncgr;
        private void NCGR_Load(object sender, EventArgs e)
        {
            ncgr = new NCGR_File();

            EndianBinaryReader br = new EndianBinaryReader(new MemoryStream(ncgr_file), Endianness.LittleEndian);

            // Lee cabecera genérica
            ncgr.Header.id = br.ReadChars(Encoding.ASCII,4);
            ncgr.Header.endianess = br.ReadUInt16();
            ncgr.Header.constant = br.ReadUInt16();
            ncgr.Header.file_size = br.ReadUInt32();
            ncgr.Header.header_size = br.ReadUInt16();
            ncgr.Header.nSection = br.ReadUInt16();

            // Lee primera sección CHAR (CHARacter data)
            ncgr.RAHC.id = br.ReadChars(Encoding.ASCII,4);
            ncgr.RAHC.size_section = br.ReadUInt32();
            ncgr.RAHC.nTilesY = br.ReadUInt16();
            ncgr.RAHC.nTilesX = br.ReadUInt16();
            ncgr.RAHC.depth = (br.ReadUInt32() == 0x3 ? ColorDepth.Depth4Bit : ColorDepth.Depth8Bit);
            ncgr.RAHC.unknown1 = br.ReadUInt16();
            ncgr.RAHC.unknown2 = br.ReadUInt16();
            ncgr.RAHC.tiledFlag = br.ReadUInt32();
            if ((ncgr.RAHC.tiledFlag & 0xFF) == 0x0)
                ncgr.orden = Orden_Tiles.Horizontal;
            else
            {
                ncgr.orden = Orden_Tiles.No_Tiles;
                if (ncgr.RAHC.nTilesX != 0xFFFF)
                {
                    ncgr.RAHC.nTilesX *= 8;
                    ncgr.RAHC.nTilesY *= 8;
                }
            }
            ncgr.RAHC.size_tiledata = br.ReadUInt32();
            ncgr.RAHC.unknown3 = br.ReadUInt32();
            ncgr.RAHC.ImageData = br.ReadBytes((int)ncgr.RAHC.size_tiledata);
            if (ncgr.Header.nSection == 1 || br.BaseStream.Position == br.BaseStream.Length)   // En caso de que no haya más secciones
            {
                br.Close();
            }
            else
            {

                // Lee la segunda sección SOPC
                ncgr.SOPC.id = br.ReadChars(Encoding.ASCII, 4);
                ncgr.SOPC.size_section = br.ReadUInt32();
                ncgr.SOPC.unknown1 = br.ReadUInt32();
                ncgr.SOPC.charSize = br.ReadUInt16();
                ncgr.SOPC.nChar = br.ReadUInt16();
                br.Close();
            }
            
            Render();
        }
        public void Render()
        {
            List<Color> c = new List<Color>();
            for (int i = 0; i < paleta.pltt.paletas.Length; i++)
            {
                c.AddRange(paleta.pltt.paletas[i].colores);
            }
            Bitmap b = new Bitmap((ncgr.RAHC.nTilesX / 64), (ncgr.RAHC.nTilesY / 64));
            int pixelnum = (ncgr.RAHC.nTilesX / 64) * (ncgr.RAHC.nTilesY / 64);
            if (ncgr.RAHC.depth == ColorDepth.Depth4Bit && ncgr.orden == Orden_Tiles.Horizontal)
            {
                byte[] b_ = ncgr.RAHC.ImageData;
                int FilePos = 0;
                for (int i = 0; i < ncgr.RAHC.size_tiledata / 64; i++)
                {
                    for (int TileY = 0; TileY < 8; TileY++)
                    {
                        for (int TileX = 0; TileX < 4; TileX++)
                        {
                            b.SetPixel(i * 8 + TileX * 2 + 1, TileY, gpalette(b_[FilePos] / 16 + (int)numericUpDown1.Value * 16, c.ToArray()));
                            b.SetPixel(i * 8 + TileX * 2, TileY, gpalette(b_[FilePos] % 16 + (int)numericUpDown1.Value * 16, c.ToArray()));
                            FilePos++;
                        }
                    }
                }
                b = CutImage(b, (int)numericUpDown2.Value, 127);
            }
            else if (ncgr.RAHC.depth == ColorDepth.Depth4Bit && ncgr.orden == Orden_Tiles.No_Tiles)
            {
                for (int j = 0; j < pixelnum; j++)
                {
                    byte[] b_ = ncgr.RAHC.ImageData;
                    uint index = b_[j / 2];
                    index = (index >> ((j % 2) << 2)) & 0x0f;
                    b.SetPixel(j - ((j / ((ncgr.RAHC.nTilesX * 8))) * ((ncgr.RAHC.nTilesX * 8))), j / ((ncgr.RAHC.nTilesX * 8)), paleta.pltt.paletas[0].colores[index]);
                }
            }
            pictureBox1.Image = b;
        }
        private Color gpalette(int n, Color[] palette)
        {
            if (n >= palette.Length)
                return Color.Pink;
            return palette[n];
        }
        public static Bitmap CutImage(Image im, int width, int blockrows)
        {
            int blocksize = im.Height / blockrows;
            int blockcount = im.Width / blocksize;

            int cols = width / blocksize;
            int rows = blockcount / cols;

            Bitmap b = new Bitmap(cols * blocksize, blockcount + 1);//rows * blocksize * blockrows);
            Graphics g = Graphics.FromImage(b);
            g.Clear(Color.Gray);

            Rectangle SourceRect = new Rectangle(0, 0, cols * blocksize, blocksize);
            Rectangle DestRect = new Rectangle(0, 0, cols * blocksize, blocksize);


            for (int r = 0; r < blockrows; r++)
            {
                SourceRect.Y = r * blocksize;
                for (int i = 0; i < rows; i++)
                {
                    SourceRect.X = i * cols * blocksize;
                    DestRect.Y = i * blocksize + r * rows * blocksize;

                    g.DrawImage(im, DestRect, SourceRect, GraphicsUnit.Pixel);
                }
            }

            return b;
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            Render();
        }

        private void numericUpDown2_ValueChanged(object sender, EventArgs e)
        {
            if (powers.Contains((int)numericUpDown2.Value) == false)
            {
                if (numericUpDown2.Value != 0)
                {
                    int diff = 8;
                    int nr = 0;
                    for (int i = 0; i < powers.Count; i++)
                    {
                        if (numericUpDown2.Value - powers[i] < diff && numericUpDown2.Value - powers[i] > 0)
                        {
                            nr = i;
                            diff = (int)numericUpDown2.Value - powers[i];
                        }
                    }
                    numericUpDown2.Value = powers[nr];
                }
                else
                {
                    numericUpDown2.Value = 8;
                }
            }
            else
            {
                Render();
            }
        }
        public static System.Collections.IEnumerable Power(int number, int exponent)
        {
            int counter = 0;
            int result = 1;
            while (counter++ < exponent)
            {
                result = result * number;
                yield return result;
            }
        }

        private void numericUpDown3_ValueChanged(object sender, EventArgs e)
        {
            if (powers.Contains((int)numericUpDown3.Value) == false)
            {
                if (numericUpDown3.Value != 0)
                {
                    int diff = 8;
                    int nr = 0;
                    for (int i = 0; i < powers.Count; i++)
                    {
                        if (numericUpDown3.Value - powers[i] < diff && numericUpDown3.Value - powers[i] > 0)
                        {
                            nr = i;
                            diff = (int)numericUpDown3.Value - powers[i];
                        }
                    }
                    numericUpDown3.Value = powers[nr];
                }
                else
                {
                    numericUpDown3.Value = 8;
                }
            }
            else
            {
                Render();
            }
        }
    }
}
